angular-touch.js ➔ getEvents   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 3
1
/**
2
 * @license AngularJS v1.8.3
3
 * (c) 2010-2020 Google LLC. http://angularjs.org
4
 * License: MIT
5
 */
6
(function(window, angular) {'use strict';
7
8
/**
9
 * @ngdoc module
10
 * @name ngTouch
11
 * @description
12
 *
13
 * The `ngTouch` module provides helpers for touch-enabled devices.
14
 * The implementation is based on jQuery Mobile touch event handling
15
 * ([jquerymobile.com](http://jquerymobile.com/)). *
16
 *
17
 * See {@link ngTouch.$swipe `$swipe`} for usage.
18
 *
19
 * @deprecated
20
 * sinceVersion="1.7.0"
21
 * The ngTouch module with the {@link ngTouch.$swipe `$swipe`} service and
22
 * the {@link ngTouch.ngSwipeLeft} and {@link ngTouch.ngSwipeRight} directives are
23
 * deprecated. Instead, stand-alone libraries for touch handling and gesture interaction
24
 * should be used, for example [HammerJS](https://hammerjs.github.io/) (which is also used by
25
 * Angular).
26
 */
27
28
// define ngTouch module
29
/* global ngTouch */
30
var ngTouch = angular.module('ngTouch', []);
31
32
ngTouch.info({ angularVersion: '1.8.3' });
33
34
function nodeName_(element) {
0 ignored issues
show
introduced by
The function nodeName_ does not seem to be used and can be removed.
Loading history...
35
  return angular.$$lowercase(element.nodeName || (element[0] && element[0].nodeName));
36
}
37
38
/* global ngTouch: false */
39
40
    /**
41
     * @ngdoc service
42
     * @name $swipe
43
     *
44
     * @deprecated
45
     * sinceVersion="1.7.0"
46
     *
47
     * See the {@link ngTouch module} documentation for more information.
48
     *
49
     * @description
50
     * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
51
     * behavior, to make implementing swipe-related directives more convenient.
52
     *
53
     * Requires the {@link ngTouch `ngTouch`} module to be installed.
54
     *
55
     * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`.
56
     *
57
     * # Usage
58
     * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
59
     * which is to be watched for swipes, and an object with four handler functions. See the
60
     * documentation for `bind` below.
61
     */
62
63
ngTouch.factory('$swipe', [function() {
64
  // The total distance in any direction before we make the call on swipe vs. scroll.
65
  var MOVE_BUFFER_RADIUS = 10;
66
67
  var POINTER_EVENTS = {
68
    'mouse': {
69
      start: 'mousedown',
70
      move: 'mousemove',
71
      end: 'mouseup'
72
    },
73
    'touch': {
74
      start: 'touchstart',
75
      move: 'touchmove',
76
      end: 'touchend',
77
      cancel: 'touchcancel'
78
    },
79
    'pointer': {
80
      start: 'pointerdown',
81
      move: 'pointermove',
82
      end: 'pointerup',
83
      cancel: 'pointercancel'
84
    }
85
  };
86
87
  function getCoordinates(event) {
88
    var originalEvent = event.originalEvent || event;
89
    var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
90
    var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0];
91
92
    return {
93
      x: e.clientX,
94
      y: e.clientY
95
    };
96
  }
97
98
  function getEvents(pointerTypes, eventType) {
99
    var res = [];
100
    angular.forEach(pointerTypes, function(pointerType) {
101
      var eventName = POINTER_EVENTS[pointerType][eventType];
102
      if (eventName) {
103
        res.push(eventName);
104
      }
105
    });
106
    return res.join(' ');
107
  }
108
109
  return {
110
    /**
111
     * @ngdoc method
112
     * @name $swipe#bind
113
     *
114
     * @description
115
     * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
116
     * object containing event handlers.
117
     * The pointer types that should be used can be specified via the optional
118
     * third argument, which is an array of strings `'mouse'`, `'touch'` and `'pointer'`. By default,
119
     * `$swipe` will listen for `mouse`, `touch` and `pointer` events.
120
     *
121
     * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
122
     * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
123
     * `event`. `cancel` receives the raw `event` as its single parameter.
124
     *
125
     * `start` is called on either `mousedown`, `touchstart` or `pointerdown`. After this event, `$swipe` is
126
     * watching for `touchmove`, `mousemove` or `pointermove` events. These events are ignored until the total
127
     * distance moved in either dimension exceeds a small threshold.
128
     *
129
     * Once this threshold is exceeded, either the horizontal or vertical delta is greater.
130
     * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
131
     * - If the vertical distance is greater, this is a scroll, and we let the browser take over.
132
     *   A `cancel` event is sent.
133
     *
134
     * `move` is called on `mousemove`, `touchmove` and `pointermove` after the above logic has determined that
135
     * a swipe is in progress.
136
     *
137
     * `end` is called when a swipe is successfully completed with a `touchend`, `mouseup` or `pointerup`.
138
     *
139
     * `cancel` is called either on a `touchcancel` or `pointercancel`  from the browser, or when we begin scrolling
140
     * as described above.
141
     *
142
     */
143
    bind: function(element, eventHandlers, pointerTypes) {
144
      // Absolute total movement, used to control swipe vs. scroll.
145
      var totalX, totalY;
146
      // Coordinates of the start position.
147
      var startCoords;
148
      // Last event's position.
149
      var lastPos;
150
      // Whether a swipe is active.
151
      var active = false;
152
153
      pointerTypes = pointerTypes || ['mouse', 'touch', 'pointer'];
154
      element.on(getEvents(pointerTypes, 'start'), function(event) {
155
        startCoords = getCoordinates(event);
156
        active = true;
157
        totalX = 0;
158
        totalY = 0;
159
        lastPos = startCoords;
160
        if (eventHandlers['start']) {
161
          eventHandlers['start'](startCoords, event);
162
        }
163
      });
164
      var events = getEvents(pointerTypes, 'cancel');
165
      if (events) {
166
        element.on(events, function(event) {
167
          active = false;
168
          if (eventHandlers['cancel']) {
169
            eventHandlers['cancel'](event);
170
          }
171
        });
172
      }
173
174
      element.on(getEvents(pointerTypes, 'move'), function(event) {
175
        if (!active) return;
176
177
        // Android will send a touchcancel if it thinks we're starting to scroll.
178
        // So when the total distance (+ or - or both) exceeds 10px in either direction,
179
        // we either:
180
        // - On totalX > totalY, we send preventDefault() and treat this as a swipe.
181
        // - On totalY > totalX, we let the browser handle it as a scroll.
182
183
        if (!startCoords) return;
184
        var coords = getCoordinates(event);
185
186
        totalX += Math.abs(coords.x - lastPos.x);
187
        totalY += Math.abs(coords.y - lastPos.y);
188
189
        lastPos = coords;
190
191
        if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
192
          return;
193
        }
194
195
        // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
196
        if (totalY > totalX) {
197
          // Allow native scrolling to take over.
198
          active = false;
199
          if (eventHandlers['cancel']) {
200
            eventHandlers['cancel'](event);
201
          }
202
          return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
203
        } else {
204
          // Prevent the browser from scrolling.
205
          event.preventDefault();
206
          if (eventHandlers['move']) {
207
            eventHandlers['move'](coords, event);
208
          }
209
        }
210
      });
211
212
      element.on(getEvents(pointerTypes, 'end'), function(event) {
213
        if (!active) return;
214
        active = false;
215
        if (eventHandlers['end']) {
216
          eventHandlers['end'](getCoordinates(event), event);
217
        }
218
      });
219
    }
220
  };
221
}]);
222
223
/* global ngTouch: false */
224
225
/**
226
 * @ngdoc directive
227
 * @name ngSwipeLeft
228
 *
229
 * @deprecated
230
 * sinceVersion="1.7.0"
231
 *
232
 * See the {@link ngTouch module} documentation for more information.
233
 *
234
 * @description
235
 * Specify custom behavior when an element is swiped to the left on a touchscreen device.
236
 * A leftward swipe is a quick, right-to-left slide of the finger.
237
 * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag
238
 * too.
239
 *
240
 * To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to
241
 * the `ng-swipe-left` or `ng-swipe-right` DOM Element.
242
 *
243
 * Requires the {@link ngTouch `ngTouch`} module to be installed.
244
 *
245
 * @element ANY
246
 * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
247
 * upon left swipe. (Event object is available as `$event`)
248
 *
249
 * @example
250
    <example module="ngSwipeLeftExample" deps="angular-touch.js" name="ng-swipe-left">
251
      <file name="index.html">
252
        <div ng-show="!showActions" ng-swipe-left="showActions = true">
253
          Some list content, like an email in the inbox
254
        </div>
255
        <div ng-show="showActions" ng-swipe-right="showActions = false">
256
          <button ng-click="reply()">Reply</button>
257
          <button ng-click="delete()">Delete</button>
258
        </div>
259
      </file>
260
      <file name="script.js">
261
        angular.module('ngSwipeLeftExample', ['ngTouch']);
262
      </file>
263
    </example>
264
 */
265
266
/**
267
 * @ngdoc directive
268
 * @name ngSwipeRight
269
 *
270
 * @deprecated
271
 * sinceVersion="1.7.0"
272
 *
273
 * See the {@link ngTouch module} documentation for more information.
274
 *
275
 * @description
276
 * Specify custom behavior when an element is swiped to the right on a touchscreen device.
277
 * A rightward swipe is a quick, left-to-right slide of the finger.
278
 * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag
279
 * too.
280
 *
281
 * Requires the {@link ngTouch `ngTouch`} module to be installed.
282
 *
283
 * @element ANY
284
 * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
285
 * upon right swipe. (Event object is available as `$event`)
286
 *
287
 * @example
288
    <example module="ngSwipeRightExample" deps="angular-touch.js" name="ng-swipe-right">
289
      <file name="index.html">
290
        <div ng-show="!showActions" ng-swipe-left="showActions = true">
291
          Some list content, like an email in the inbox
292
        </div>
293
        <div ng-show="showActions" ng-swipe-right="showActions = false">
294
          <button ng-click="reply()">Reply</button>
295
          <button ng-click="delete()">Delete</button>
296
        </div>
297
      </file>
298
      <file name="script.js">
299
        angular.module('ngSwipeRightExample', ['ngTouch']);
300
      </file>
301
    </example>
302
 */
303
304
function makeSwipeDirective(directiveName, direction, eventName) {
305
  ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) {
306
    // The maximum vertical delta for a swipe should be less than 75px.
307
    var MAX_VERTICAL_DISTANCE = 75;
308
    // Vertical distance should not be more than a fraction of the horizontal distance.
309
    var MAX_VERTICAL_RATIO = 0.3;
310
    // At least a 30px lateral motion is necessary for a swipe.
311
    var MIN_HORIZONTAL_DISTANCE = 30;
312
313
    return function(scope, element, attr) {
314
      var swipeHandler = $parse(attr[directiveName]);
315
316
      var startCoords, valid;
317
318
      function validSwipe(coords) {
319
        // Check that it's within the coordinates.
320
        // Absolute vertical distance must be within tolerances.
321
        // Horizontal distance, we take the current X - the starting X.
322
        // This is negative for leftward swipes and positive for rightward swipes.
323
        // After multiplying by the direction (-1 for left, +1 for right), legal swipes
324
        // (ie. same direction as the directive wants) will have a positive delta and
325
        // illegal ones a negative delta.
326
        // Therefore this delta must be positive, and larger than the minimum.
327
        if (!startCoords) return false;
328
        var deltaY = Math.abs(coords.y - startCoords.y);
329
        var deltaX = (coords.x - startCoords.x) * direction;
330
        return valid && // Short circuit for already-invalidated swipes.
331
            deltaY < MAX_VERTICAL_DISTANCE &&
332
            deltaX > 0 &&
333
            deltaX > MIN_HORIZONTAL_DISTANCE &&
334
            deltaY / deltaX < MAX_VERTICAL_RATIO;
335
      }
336
337
      var pointerTypes = ['touch'];
338
      if (!angular.isDefined(attr['ngSwipeDisableMouse'])) {
339
        pointerTypes.push('mouse');
340
      }
341
      $swipe.bind(element, {
342
        'start': function(coords, event) {
343
          startCoords = coords;
344
          valid = true;
345
        },
346
        'cancel': function(event) {
347
          valid = false;
348
        },
349
        'end': function(coords, event) {
350
          if (validSwipe(coords)) {
351
            scope.$apply(function() {
352
              element.triggerHandler(eventName);
353
              swipeHandler(scope, {$event: event});
354
            });
355
          }
356
        }
357
      }, pointerTypes);
358
    };
359
  }]);
360
}
361
362
// Left is negative X-coordinate, right is positive.
363
makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
364
makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
365
366
367
368
})(window, window.angular);
369